#include <iostream>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>

using namespace std;


//之前我们使用过wait和waitpid函数清理僵尸进程
//父进程可以阻塞等待子进程结束,也可以非阻塞地查询是否有子进程结束等待清理(也就是轮询的方式)
//采用第一种方式,父进程阻塞了就不能处理自己的工作了
//采用第二种方式,父进程在处理自己的工作的同时还要记得时不时地轮询一 下,程序实现复杂
//
//其实,子进程在终止时会给父进程发SIGCHLD信号,该信号的默认处理动作是忽略
//父进程可以自定义SIGCHLD信号的处理函数,这样父进程只需专心处理自己的工作
//不必关心子进程了,子进程终止时会通知父进程
//父进程在信号处理函数中调用wait清理子进程即可
//
//要想不产生僵尸进程,还有另一种方法:父进程将SIGCHLD的处理动作设置为SIG_IGN
//一般情况下SIG_IGN的默认处理动作就是忽略,和SIGCHLD的默认处理忽略,理应是一样的
//但是这是一个特殊的例子,SIGCHLD的处理动作设置为SIG_IGN之后
//fork出来的子进程会在终止的时候自动清理掉,不会产生僵尸进程
//也不会通知父进程(父进程拿不到子进程的各种信息了,比如退出码和退出状态)
//这个特例只有linux可用,但不能保证其他UNIX系统上都可用

//自动释放僵尸进程
//int main()
//{
//    //显式设置忽略17好信号,当进程推出后,自动释放僵尸进程
//    signal(SIGCHLD, SIG_IGN);
//    pid_t id = fork();
//    if (id == 0)
//    {
//        int count = 3;
//        while(count--)
//        {
//            cout << "i am child process" << endl;
//            sleep(1);
//        }
//        exit(0);
//    }
//    while(true)
//    {
//        cout << "i am parent process, pid = " << getpid() << endl;   
//        sleep(1);
//    }
//    return 0;
//}

//从下面的程序中探索一下子进程和父进程接收信号的关系
//下面我将详细的流程和结果阐述如下:
//当进程运行的时候,孙进程没有进行信号的注册
//子进程和父进程进行了信号的注册
//那么我们处于终端的前台,这个时候我们按下ctrl+c,向三个进程同时发送了2号信号,注意这里不是只给父进程发送2号信号,而是三个都发送了
//而kill可以给每个进程单独发送信号
//接收到信号之后,孙进程没有进行过信号的注册,这个时候,收到二号信号执行退出的动作
//退出后,将17号信号发送给子进程,然后子进程接收到17信号,执行自定义动作
//这个时候子进程已经接收到了2号信号和17号信号,从内核态返回用户态的时候,
//由于17号信号的优先级比较高,所以优先处理它
//这个时候父进程已经在处理2号信号了
//子进程处理好了17号之后,然后再执行2号信号
//void handler(int signo)
//{
//    cout << "signo:" << signo << "   id" << getpid() << endl; 
//    sleep(3);
//}
//
//int main()
//{
//    pid_t cid = fork();
//
//    if (cid == 0)
//    {
//
//        pid_t cid1 = fork();
//        if (cid1 == 0)
//        {
//            //孙进程
//            int count = 50;
//            cout << "child:" << getpid() << endl;
//            while(count--)
//            {
//                cout << " " << endl;
//                sleep(1);
//            }
//            exit(1);
//        }
//        //子进程
//        //因为子进程和父进程之间的信号捕捉是共享的,所以需要把信号捕捉函数放到这里
//        signal(17, handler);
//        signal(2, handler);
//
//        int count = 50;
//        cout << "child:" << getpid() << endl;
//        while(count--)
//        {
//            cout << " " << endl;
//            sleep(1);
//        }
//        exit(1);
//    }
//
//    //父进程
//    signal(17, handler);
//    signal(2, handler);
//
//    while(1)
//    {
//        cout << "parent process" << endl;
//        sleep(1);
//    }
//    return 0;
//}

//请编写一个程序完成以下功能:父进程fork出子进程,子进程调用exit(1)终止
//父进程自定义SIGCHLD信号的处理函数,在其中调用wait获得子进程的退出状态并打印
void handler(int signo)
{
    pid_t id = 0;
    int status = 0;
    //这里写while循环以及WNOHANG的意义
    //如果多个子进程同时退出了,那么父进程只会收到一个信号
    //所以这里循环可以进行多个子进程的处理
    //如果这里不设置WNOHANG,那么多个子进程只退出了其中的一部分
    //那么剩下的没退出的,waitpid就会进行阻塞
    while((id = waitpid(-1, &status, WNOHANG)) > 0)
    {
        cout << "wait child success, exit code:" << ((status >> 8) & 0XFF) << " exit signal:" << (status & 0X7F) << endl;
    }
}

int main()
{
    signal(SIGCHLD, handler);
    pid_t cid = fork();
    if (cid == 0)
    {
        cout << "child:" << getpid() << endl;
        sleep(3);
        exit(1);
    }

    while(1)
    {
        cout << "parent process" << endl;
        sleep(1);
    }

    return 0;
}
